﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using hlavny.modul2;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using hlavny.modul4;

namespace hlavny
{

    class cIndIndexFile
    {
        /// <summary>
        /// Zoznam indexov pre jednotlive stlpce, kluc je cislo stlpca zo syscolumns
        /// </summary>
        Dictionary<int, cIndHashFile> paIndexes = new Dictionary<int,cIndHashFile>();
        
        public cIndHashFile this[int index]
        {
            get
            {
                return paIndexes[index];
            }
        }

        /// <summary>
        /// Aktualny index
        /// </summary>
        private int piActiveIndex = -1;

        public int iActiveIndex
        {
            get { return piActiveIndex; }
            set { piActiveIndex = value; }
        }

        public cIndIndexFile()
        {
            if (File.Exists("hashIndex.sav") == true)
            {
                bool bSuccess = true;
                FileStream stream = null;
                try
                {
                    stream= new FileStream("hashIndex.sav", FileMode.Open);
                    BinaryFormatter binary = new BinaryFormatter();
                    paIndexes = binary.Deserialize(stream) as Dictionary<int, cIndHashFile>;
                    foreach (cIndHashFile oFile in paIndexes.Values)
                    {
                        oFile.reloadFile();
                    }
                }
                catch (Exception)
                {

                }
                finally
                {
                    if (bSuccess == false)
                    {
                        paIndexes = new Dictionary<int, cIndHashFile>();
                    }
                    stream.Close();
                }

                
            }
        }

        public bool containsIndex(int pa_iColumnNo)
        {
            return paIndexes.Keys.Contains(pa_iColumnNo);
            
        }
        /// <summary>
        /// Vytvori novy index
        /// </summary>
        /// <param name="pa_sFileName">Nazov suboru</param>
        /// <param name="pa_iColumn">Cislo stlpca</param>
        /// <param name="pa_iTableNo">Cislo tabulky</param>
        /// <param name="pa_iNumberOfRecordsInBucket">Pocet prvkov v buckete</param>
        /// <param name="pa_iUsedHash">Pouzita hash funkcia</param>
        public void createIndex(string pa_sFileName, int pa_iColumn, int pa_iTableNo, int pa_iNumberOfRecordsInBucket, int pa_iUsedHash)
        {
            string sColumnName = "hlavny.modul1." + CDBS.SysColumns.Columns[CDBS.SysColumns.GetAttributeIndex(pa_iColumn)].Type.ToString();
            int iLength = CDBS.SysColumns.Columns[CDBS.SysColumns.GetAttributeIndex(pa_iColumn)].Length;
            Type oType = Type.GetType(sColumnName, false);
            object[] oArgs;
            switch (sColumnName)
            {
                case "hlavny.modul1.Character":
                    oArgs = new object[2];
                    oArgs[0] = "";
                    oArgs[1] = iLength;
                    break;
                case "hlavny.modul1.Boolean":
                    oArgs = new object[1];
                    oArgs[0] = true;
                    break;
                default:
                    oArgs = new object[1];
                    oArgs[0] = 0;
                    break;
            }
            paIndexes.Add(pa_iColumn, new cIndHashFile(pa_sFileName, ((modul1.Object)Activator.CreateInstance(oType, oArgs)).GetLength(), pa_iNumberOfRecordsInBucket, sColumnName, iLength, pa_iUsedHash));
            DataManager dataManager = new DataManager(CDBS.SysTables, CDBS.SysColumns, pa_iTableNo);
            dataManager.VynulujAktualnuPoziciu();
            Zaznam zaznam;
            // ak treba, tak naplni index hodnotami v subore
            while ((zaznam = dataManager.DajDalsi()) != null)
            {
                insertIndex(zaznam.GetValue(pa_iColumn), zaznam.RowId, pa_iColumn, new List<int>());
            }

            save();
        }

        /// <summary>
        /// Zmaze index na danom stlpci
        /// </summary>
        /// <param name="pa_iColumnNo"></param>
        /// <returns></returns>
        public bool dropIndex(int pa_iColumnNo)
        {
            bool bSuccess = true;
            try
            {
                File.Delete(paIndexes[pa_iColumnNo].sFileName);
                File.Delete(paIndexes[pa_iColumnNo].sAddressFileName);
                paIndexes.Remove(pa_iColumnNo);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.Message);
                bSuccess = false;
            }
            save();
            return bSuccess;
        }

        /// <summary>
        /// Vlozenie novej hodnoty
        /// </summary>
        /// <param name="pa_oValue"></param>
        /// <param name="pa_iColumnNo"></param>
        /// <returns></returns>
        public bool insertIndex(modul1.Object pa_oValue, long pa_iRowid, int pa_iColumnNo, List<int> pa_iReferenceCols)
        {
            if (pa_iReferenceCols != null && pa_iReferenceCols.Count > 0)
            {
                foreach (int iId in pa_iReferenceCols)
                {
                    if (paIndexes.Keys.Contains(iId) == true)
                    {
                        if (selectKey(iId, pa_oValue) == -1)
                        {
                            return false;
                        }
                    }
                }
            }
            bool isNew = paIndexes[pa_iColumnNo].findKey(pa_oValue.ToByte()) == -1 ? true : false;
            paIndexes[pa_iColumnNo].insertValue(pa_oValue.ToByte(), pa_iRowid);
            paIndexes[pa_iColumnNo].addValue(pa_oValue);
            if (isNew == true)
            {
                paIndexes[pa_iColumnNo].iDifValues++;
            }
            return false;
        }


        /// <summary>
        /// Najde kluc
        /// </summary>
        /// <param name="pa_iColumnNo"></param>
        /// <param name="pa_oValue"></param>
        /// <returns></returns>
        public long selectKey(int pa_iColumnNo, modul1.Object pa_oValue)
        {
            return paIndexes[pa_iColumnNo].findKey(pa_oValue.ToByte());
        }

        /// <summary>
        /// Najde dalsi kluc s danou hodnotou
        /// </summary>
        /// <param name="pa_iColumnNo"></param>
        /// <param name="pa_oValue"></param>
        /// <returns></returns>
        public long selectNextKey(int pa_iColumnNo, modul1.Object pa_oValue)
        {
            return paIndexes[pa_iColumnNo].findNextKey(pa_oValue.ToByte());
        }

        /// <summary>
        /// Najde dalsi kluc s danou hodnotou
        /// </summary>
        /// <param name="pa_iColumnNo"></param>
        /// <param name="pa_oValue"></param>
        /// <returns></returns>
        public bool isKey(int pa_iColumnNo, modul1.Object pa_oValue)
        {
            if (paIndexes[pa_iColumnNo].findKey(pa_oValue.ToByte()) == -1)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// Vymaze hodnotu
        /// </summary>
        /// <param name="pa_iColumnNo"></param>
        /// <param name="pa_iRowid"></param>
        /// <returns></returns>
        public bool deleteIndex(int pa_iColumnNo, modul1.Object pa_oValue, long pa_iRowid)
        {
            bool bReturn = paIndexes[pa_iColumnNo].deleteValue(pa_oValue.ToByte(), pa_iRowid);
            if (paIndexes[pa_iColumnNo].bUseImmediateStats == true)
            {
                paIndexes[pa_iColumnNo].removeValue(pa_oValue);
                if(paIndexes[pa_iColumnNo].findKey(pa_oValue.ToByte()) == -1)
                {
                    paIndexes[pa_iColumnNo].iDifValues--;
                }
            }
            save();
            return bReturn;
        }

        /// <summary>
        /// Updatuje hodnotu
        /// </summary>
        /// <param name="pa_iColumnNo">Cislo stlpca</param>
        /// <param name="pa_oValue">Hodnota</param>
        /// <param name="pa_iRowid">Rowid</param>
        /// <param name="pa_oNewValue">Nova hodnota</param>
        /// <param name="pa_iNewRowid">Nove rowid</param>
        /// <returns></returns>
        public bool updateIndex(int pa_iColumnNo, modul1.Object pa_oValue, long pa_iRowid, modul1.Object pa_oNewValue, long pa_iNewRowid)
        {
            bool bReturn;
            bool bFound;
            if(paIndexes[pa_iColumnNo].findKey(pa_oNewValue.ToByte()) != -1)
            {
                bFound = true;
            }
            else
            {
               bFound = false;
            }
            bReturn = paIndexes[pa_iColumnNo].updateValue(pa_oValue.ToByte(), pa_oNewValue.ToByte(), pa_iRowid, pa_iNewRowid);
            if (paIndexes[pa_iColumnNo].bUseImmediateStats == true)
            {
                if(pa_oNewValue != pa_oValue )
                {
                    paIndexes[pa_iColumnNo].removeValue(pa_oValue);
                    paIndexes[pa_iColumnNo].addValue(pa_oNewValue);
                    if(paIndexes[pa_iColumnNo].findKey(pa_oValue.ToByte()) == -1)
                    {
                        paIndexes[pa_iColumnNo].iDifValues--;
                    }
                    if(bFound == false)
                    {
                        paIndexes[pa_iColumnNo].iDifValues++;
                    }

                }
            }
            save();
            return bReturn;
        }


        /// <summary>
        /// Vypis indexu do textovej podoby
        /// </summary>
        /// <param name="pa_iColumnNo"></param>
        /// <param name="pa_sFileName"></param>
        public void infoText(int pa_iColumnNo, string pa_sFileName)
        {
            paIndexes[pa_iColumnNo].toTextFile(pa_sFileName);
        }

        /// <summary>
        /// Zisti statistiky
        /// </summary>
        /// <param name="pa_iColumnNo">Cislo stlpca</param>
        /// <param name="pa_bWantHistogram">Ci chcem alebo nechcem histogram</param>
        /// <param name="pa_iHistogramCols">Pocet stplcov histogramu</param>
        /// <param name="pa_iFillMinVals">Ci sa maju alebo nemaju naplnit statistiky suboru</param>
        /// <returns>Hodnotu cIndStats naplnenu udajmi</returns>
        public cIndStats getIndexStats(int pa_iColumnNo, bool pa_bWantHistogram, int pa_iHistogramCols, bool pa_iFillMinVals)
        {
            cIndStats oStats = new cIndStats();
            if (paIndexes[pa_iColumnNo].bUseImmediateStats == true)
            {
                oStats.oMax = paIndexes[pa_iColumnNo].oMaxValue;
                oStats.oMin = paIndexes[pa_iColumnNo].oMinValue;
                oStats.iMainBlocksUsage = paIndexes[pa_iColumnNo].iMainBlocksUsage;
                oStats.iOverloadBlocksUsage = paIndexes[pa_iColumnNo].iOverloadBlocksUsage;
                oStats.iUniqueVals = paIndexes[pa_iColumnNo].iDifValues;
                if (pa_bWantHistogram == true)
                {
                    oStats.aHistogram = paIndexes[pa_iColumnNo].getHistogram(oStats.oMin, oStats.oMax, pa_iHistogramCols);
                }
                else
                {
                    oStats.aHistogram = null;
                }
            }
            else
            {
                hlavny.modul1.Object poMin;
                hlavny.modul1.Object poMax;
                float piMainBlocksUsage;
                float piOverloadBlocksUsage;
                int piUniqueVals;
                List<cHistogramColumn> paHistogram;
                paIndexes[pa_iColumnNo].analyze(out piMainBlocksUsage, out piOverloadBlocksUsage, out poMax, out poMin, out piUniqueVals, pa_iHistogramCols, out paHistogram, pa_iFillMinVals);
                oStats.oMax = poMax;
                oStats.oMin = poMin;
                oStats.iMainBlocksUsage = piMainBlocksUsage;
                oStats.iOverloadBlocksUsage = piOverloadBlocksUsage;
                oStats.aHistogram = paHistogram;
                oStats.iUniqueVals = piUniqueVals;
            }

            return oStats;

        }
        /// <summary>
        /// Upravi pouzivane statistiky indexu
        /// </summary>
        /// <param name="pa_iColumnNo">Cislo stlpca</param>
        /// <param name="pa_bUseImmediateStats">Ci sa maju pouzivat okamzite statistiky</param>
        public void changeStatisticsUsage(int pa_iColumnNo, bool pa_bUseImmediateStats)
        {
            if (paIndexes[pa_iColumnNo].bUseImmediateStats == pa_bUseImmediateStats)
            {
                return;
            }
            else
            {
                if (paIndexes[pa_iColumnNo].bUseImmediateStats == true && pa_bUseImmediateStats == false)
                {
                    paIndexes[pa_iColumnNo].bUseImmediateStats = false;
                }
                else
                {
                    paIndexes[pa_iColumnNo].bUseImmediateStats = true;
                    hlavny.modul1.Object poMin;
                    hlavny.modul1.Object poMax;
                    float piMainBlocksUsage;
                    float piOverloadBlocksUsage;
                    int piUniqueVals;
                    List<cHistogramColumn> paHistogram;
                    paIndexes[pa_iColumnNo].analyze(out piMainBlocksUsage, out piOverloadBlocksUsage, out poMax, out poMin, out piUniqueVals, 10, out paHistogram, true);
                    paIndexes[pa_iColumnNo].oMaxValue = poMax;
                    paIndexes[pa_iColumnNo].oMinValue = poMin;
                    paIndexes[pa_iColumnNo].iDifValues = piUniqueVals;
                }
            }
            save();
        }
        /// <summary>
        /// Zoserializuje subor
        /// </summary>
        public void save()
        {
            FileStream stream = new FileStream("hashIndex.sav", FileMode.Create);
            BinaryFormatter binary = new BinaryFormatter();
            binary.Serialize(stream, paIndexes);
            stream.Close();
        }

    }
}
